package com.dbsop.operation.field;

import com.dbsop.domain.info.EsFiledInfo;
import com.dbsop.exception.EsRunException;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.*;
import org.elasticsearch.cluster.metadata.MappingMetaData;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;

import java.io.IOException;
import java.util.*;

/**
 * Rest 操作字段
 */
@Slf4j
public class RestFieldOperation implements FieldOperation {

    private RestHighLevelClient rc;

    public RestFieldOperation(RestHighLevelClient restClient) {
        this.rc = restClient;
    }

    public boolean checkField(Map<String,Object> properties,EsFiledInfo esFiledInfo){
        String type = (String) properties.get("type");
        Map<String,Object> subProperties = (Map<String, Object>) properties.get("properties");
        if (type == null && subProperties!=null &&  esFiledInfo.getChildFields()!=null){
            List<EsFiledInfo> childFields = esFiledInfo.getChildFields();

            for (EsFiledInfo childField : childFields) {
                String esFieldName = childField.getEsFieldName();
                Map<String,Object> childSub = (Map<String, Object>) subProperties.get("properties");
                if (childSub == null){
                    childSub = (Map<String, Object>) subProperties.get(childField.getEsFieldName());
                    if (childSub !=null && checkField(childSub,childField) ){
                            continue;
                    }else {
                        log.info("字段信息映射不符:"+childField.getFieldName());
                        return false;
                    }

                }
                if (childSub == null){
                    log.info("字段信息映射不符:"+childField.getFieldName());
                    return false;
                }
                Map<String,Object> esProSubProperties = (Map<String,Object>) childSub.get(esFieldName);
                if (esProSubProperties == null){
                    log.info("字段信息映射不符:"+childField.getFieldName());
                    return false;
                }
                if (!checkField(esProSubProperties,esFiledInfo)){
                    log.info("字段信息映射不符:"+childField.getFieldName());
                    return false;
                }
            }
            return true;
        }else if (esFiledInfo.getEsFieldType().equals(properties.get("type"))){
            return true;
        }
        log.info("字段信息映射不符:"+esFiledInfo.getFieldName());
        return false;
    }

    @Override
    public Boolean checkIndexFields(String indexName, Collection<EsFiledInfo> esFiledInfoList) {
        Map<String, Map<String,Object>> esFieldMap = getFields(indexName);
        for (EsFiledInfo esFiledInfo : esFiledInfoList) {
            Map<String, Object> properties = esFieldMap.get(esFiledInfo.getEsFieldName());
            if (properties == null){
                return false;
            }

            if (!checkField(properties,esFiledInfo)){
                return false;
            }
        }

        return true;
    }

    @Override
    public void checkContain(String indexName, Collection<EsFiledInfo> esFiledInfoList) {
        Map<String, Map<String,Object>> esFieldMap = getFields(indexName);
        for (EsFiledInfo esFiledInfo : esFiledInfoList) {
            Map<String, Object> properties = esFieldMap.get(esFiledInfo.getEsFieldName());
            if (properties == null)throw new EsRunException("ES field:" + esFiledInfo.getEsFieldName() + " Not Found !!!");
            boolean checkField = checkField(properties, esFiledInfo);
            if (!checkField){
                throw new EsRunException("ES field:" + esFiledInfo.getEsFieldName() + " FIELD INCONFORMITY !!!!");
            }
        }
    }

    @Override
    public Map<String, Map<String, Object>> getFields(String indexName) {
        Map<String,Map<String, Object>> fieldMap = new LinkedHashMap<String, Map<String, Object>>();


        GetMappingsRequest getMappingsRequest = new GetMappingsRequest();
        getMappingsRequest.indices(indexName);

        try {
            GetMappingsResponse mapping = rc.indices().getMapping(getMappingsRequest, RequestOptions.DEFAULT);
            Map<String, Object> sourceAsMap = mapping.mappings().get(indexName).sourceAsMap();
            if (sourceAsMap!=null && sourceAsMap.size()>0){
                Map<String,Object> properties = (Map<String, Object>) sourceAsMap.get("properties");
                if (properties!=null && properties.size()>0){
                    for (String key :properties.keySet()) {
                      fieldMap.put(key, (Map<String, Object>) properties.get(key));
                    }
                }

            }

        } catch (IOException e) {
            e.printStackTrace();
        }
        return fieldMap;
    }


    private void loadingXContentBuilder(Map<String,Object> properties, Collection<EsFiledInfo> esFiledInfoList){
            for (EsFiledInfo esFiledInfo : esFiledInfoList) {
                String esFieldType = esFiledInfo.getEsFieldType();
                String esFieldName = esFiledInfo.getEsFieldName();
                Map<String,Object> esProperty = new LinkedHashMap<String, Object>();
                esProperty.put("type",esFieldType);
                if (esFiledInfo.getAnalyzer()!=null){
                    esProperty.put("analyzer",esFiledInfo.getAnalyzer());
                }
                properties.put(esFieldName,esProperty);
                List<EsFiledInfo> childFields = esFiledInfo.getChildFields();
                if (childFields!=null){
                    Map<String,Object> suProperties = new LinkedHashMap<String, Object>();
                    esProperty.put("properties",suProperties);
                    loadingXContentBuilder(suProperties, childFields);
                }
            }
    }

    @Override
    public void createIndexFiled(String indexName, Collection<EsFiledInfo> esFiledInfoList) {
        try {
            Map<String,Object> properties = new LinkedHashMap<String, Object>();
            loadingXContentBuilder(properties,esFiledInfoList);
            Map<String, Object> indexMapping = new HashMap<>();
            indexMapping.put("properties", properties);
            PutMappingRequest request = new PutMappingRequest(indexName);
            request.source(indexMapping);
            boolean acknowledged = rc.indices().putMapping(request, RequestOptions.DEFAULT).isAcknowledged();
            if (acknowledged) log.info("索引添加字段成功: " + indexName);
            else log.info("索引添加字段失败: " + indexName);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
